use super::raw::*;

pub trait Endian {
    fn reverse(&self) -> Self;
    fn map(&self, host_endian: bool) -> Self where Self: Clone {
        if host_endian {
            self.clone()
        } else {
            self.reverse()
        }
    }
}

impl Endian for u8 {
    fn reverse(&self) -> Self {
        *self
    }
}

impl Endian for u16 {
    #[cfg(target_endian = "big")]
    fn reverse(&self) -> Self {
        u16::from_le_bytes((*self).to_ne_bytes())
    }
    #[cfg(target_endian = "little")]
    fn reverse(&self) -> Self {
        u16::from_be_bytes((*self).to_ne_bytes())
    }
}

impl Endian for u32 {
    #[cfg(target_endian = "big")]
    fn reverse(&self) -> Self {
        u32::from_le_bytes((*self).to_ne_bytes())
    }
    #[cfg(target_endian = "little")]
    fn reverse(&self) -> Self {
        u32::from_be_bytes((*self).to_ne_bytes())
    }
}

impl Endian for i32 {
    #[cfg(target_endian = "big")]
    fn reverse(&self) -> Self {
        i32::from_le_bytes((*self).to_ne_bytes())
    }
    #[cfg(target_endian = "little")]
    fn reverse(&self) -> Self {
        i32::from_be_bytes((*self).to_ne_bytes())
    }
}

impl Endian for u64 {
    #[cfg(target_endian = "big")]
    fn reverse(&self) -> Self {
        u64::from_le_bytes((*self).to_ne_bytes())
    }
    #[cfg(target_endian = "little")]
    fn reverse(&self) -> Self {
        u64::from_be_bytes((*self).to_ne_bytes())
    }
}

impl Endian for i64 {
    #[cfg(target_endian = "big")]
    fn reverse(&self) -> Self {
        i64::from_le_bytes((*self).to_ne_bytes())
    }
    #[cfg(target_endian = "little")]
    fn reverse(&self) -> Self {
        i64::from_be_bytes((*self).to_ne_bytes())
    }
}

impl Endian for Elf32_Ehdr {
    fn reverse(&self) -> Self {
        Self {
            e_ident: self.e_ident,
            e_type: self.e_type.reverse(),
            e_machine: self.e_machine.reverse(),
            e_version: self.e_version.reverse(),
            e_entry: self.e_entry.reverse(),
            e_phoff: self.e_phoff.reverse(),
            e_shoff: self.e_shoff.reverse(),
            e_flags: self.e_flags.reverse(),
            e_ehsize: self.e_ehsize.reverse(),
            e_phentsize: self.e_phentsize.reverse(),
            e_phnum: self.e_phnum.reverse(),
            e_shentsize: self.e_shentsize.reverse(),
            e_shnum: self.e_shnum.reverse(),
            e_shstrndx: self.e_shstrndx.reverse(),
        }
    }
}

impl Endian for Elf64_Ehdr {
    fn reverse(&self) -> Self {
        Self {
            e_ident: self.e_ident,
            e_type: self.e_type.reverse(),
            e_machine: self.e_machine.reverse(),
            e_version: self.e_version.reverse(),
            e_entry: self.e_entry.reverse(),
            e_phoff: self.e_phoff.reverse(),
            e_shoff: self.e_shoff.reverse(),
            e_flags: self.e_flags.reverse(),
            e_ehsize: self.e_ehsize.reverse(),
            e_phentsize: self.e_phentsize.reverse(),
            e_phnum: self.e_phnum.reverse(),
            e_shentsize: self.e_shentsize.reverse(),
            e_shnum: self.e_shnum.reverse(),
            e_shstrndx: self.e_shstrndx.reverse(),
        }
    }
}

impl Endian for Elf32_Shdr {
    fn reverse(&self) -> Self {
        Self {
            sh_name: self.sh_name.reverse(),
            sh_type: self.sh_type.reverse(),
            sh_flags: self.sh_flags.reverse(),
            sh_addr: self.sh_addr.reverse(),
            sh_offset: self.sh_offset.reverse(),
            sh_size: self.sh_size.reverse(),
            sh_link: self.sh_link.reverse(),
            sh_info: self.sh_info.reverse(),
            sh_addralign: self.sh_addralign.reverse(),
            sh_entsize: self.sh_entsize.reverse(),
        }
    }
}

impl Endian for Elf64_Shdr {
    fn reverse(&self) -> Self {
        Self {
            sh_name: self.sh_name.reverse(),
            sh_type: self.sh_type.reverse(),
            sh_flags: self.sh_flags.reverse(),
            sh_addr: self.sh_addr.reverse(),
            sh_offset: self.sh_offset.reverse(),
            sh_size: self.sh_size.reverse(),
            sh_link: self.sh_link.reverse(),
            sh_info: self.sh_info.reverse(),
            sh_addralign: self.sh_addralign.reverse(),
            sh_entsize: self.sh_entsize.reverse(),
        }
    }
}

impl Endian for Elf32_Sym {
    fn reverse(&self) -> Self {
        Self {
            st_name: self.st_name.reverse(),
            st_value: self.st_value.reverse(),
            st_size: self.st_size.reverse(),
            st_shndx: self.st_shndx.reverse(),
            ..*self
        }
    }
}

impl Endian for Elf64_Sym {
    fn reverse(&self) -> Self {
        Self {
            st_name: self.st_name.reverse(),
            st_value: self.st_value.reverse(),
            st_size: self.st_size.reverse(),
            st_shndx: self.st_shndx.reverse(),
            ..*self
        }
    }
}

impl Endian for Elf32_Phdr {
    fn reverse(&self) -> Self {
        Self {
            ph_type: self.ph_type.reverse(),
            ph_offset: self.ph_offset.reverse(),
            ph_vaddr: self.ph_vaddr.reverse(),
            ph_paddr: self.ph_paddr.reverse(),
            ph_filesz: self.ph_filesz.reverse(),
            ph_memsz: self.ph_memsz.reverse(),
            ph_flags: self.ph_flags.reverse(),
            ph_align: self.ph_align.reverse(),
        }
    }
}

impl Endian for Elf64_Phdr {
    fn reverse(&self) -> Self {
        Self {
            ph_type: self.ph_type.reverse(),
            ph_flags: self.ph_flags.reverse(),
            ph_offset: self.ph_offset.reverse(),
            ph_vaddr: self.ph_vaddr.reverse(),
            ph_paddr: self.ph_paddr.reverse(),
            ph_filesz: self.ph_filesz.reverse(),
            ph_memsz: self.ph_memsz.reverse(),
            ph_align: self.ph_align.reverse(),
        }
    }
}

impl Endian for Elf32_Rel {
    fn reverse(&self) -> Self {
        Self {
            r_offset: self.r_offset.reverse(),
            r_info: self.r_info.reverse(),
        }
    }
}

impl Endian for Elf32_Rela {
    fn reverse(&self) -> Self {
        Self {
            r_offset: self.r_offset.reverse(),
            r_info: self.r_info.reverse(),
            r_addend: self.r_addend.reverse(),
        }
    }
}

impl Endian for Elf64_Rel {
    fn reverse(&self) -> Self {
        Self {
            r_offset: self.r_offset.reverse(),
            r_info: self.r_info.reverse(),
        }
    }
}

impl Endian for Elf64_Rela {
    fn reverse(&self) -> Self {
        Self {
            r_offset: self.r_offset.reverse(),
            r_info: self.r_info.reverse(),
            r_addend: self.r_addend.reverse(),
        }
    }
}

impl Endian for Elf_GnuHash {
    fn reverse(&self) -> Self {
        Self {
            gh_nbuckets: self.gh_nbuckets.reverse(),
            gh_symndx: self.gh_symndx.reverse(),
            gh_bmmask: self.gh_bmmask.reverse(),
            gh_bmshift: self.gh_bmshift.reverse(),
        }
    }
}

impl Endian for Elf32_Dyn {
    fn reverse(&self) -> Self {
        Self {
            d_tag: self.d_tag.reverse(),
            d_val_ptr: self.d_val_ptr.reverse(),
        }
    }
}

impl Endian for Elf64_Dyn {
    fn reverse(&self) -> Self {
        Self {
            d_tag: self.d_tag.reverse(),
            d_val_ptr: self.d_val_ptr.reverse(),
        }
    }
}
